package com.hyt.it.ogt.kq.service.gov.aspect;

import com.alibaba.fastjson.JSON;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.it.ogt.kq.common.gov.annotation.NoRepeatCommit;
import com.hyt.it.ogt.kq.common.gov.constant.ResponseCode;
import com.hyt.it.ogt.kq.common.gov.enums.CacheKeyEnum;
import com.hyt.loginfo.service.IBaseLoginService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Slf4j
@Aspect
@Component
public class NoRepeatCommitAspect {

    @Resource
    private IBaseLoginService iBaseLoginService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 环绕
     * @param proceedingJoinPoint 连接点
     * @return 结果
     * @throws Throwable 异常
     */
    @Around(value = "@annotation(com.hyt.it.ogt.kq.common.gov.annotation.NoRepeatCommit)")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //获取访问的方法
        Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
        //获取注解参数
        NoRepeatCommit noRepeat = method.getAnnotation(NoRepeatCommit.class);
        //获取key
        String key = convertKey(proceedingJoinPoint, noRepeat);
        //获取锁
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, true,
                noRepeat.leaseTime(), TimeUnit.SECONDS);
        //校验锁
        Optional<Boolean> optional = Optional.ofNullable(flag);
        if (optional.isPresent()) {
            if (optional.get()) {
                try {
                    //执行主线程
                    return proceedingJoinPoint.proceed();
                } finally {
                    Boolean success = redisTemplate.delete(key);
                    success = Optional.ofNullable(success).orElse(false);
                    if (!success) {
                        log.error("清除分布式锁失败，或锁已不存在，key值: {}", key);
                    }
                }
            } else {
                //重复的请求
                log.info("重复的请求，key值: {}", key);
                return ApiResponse.builder()
                        .code(ResponseCode.REPEAT_COMMIT.getCode())
                        .build();
            }
        }
        //未知原因
        log.info("获取锁失败，未知原因，key值: {}", key);
        return ApiResponse.builder()
                .code(ResponseCode.SUCCESS.getCode())
                .data("未知原因")
                .build();
    }

    /**
     * 组装key
     * @param proceedingJoinPoint 连接点
     * @param noRepeat 注解
     * @return key
     */
    private String convertKey(ProceedingJoinPoint proceedingJoinPoint, NoRepeatCommit noRepeat) {
        //获取请求参数和请求参数值
        Object[] paramValues = proceedingJoinPoint.getArgs();
        String[] paramNames = ((CodeSignature) proceedingJoinPoint.getSignature()).getParameterNames();
        //将请求参数转为Map，String是参数名，Object是参数值
        Map<String, Object> paramMap = new HashMap<>();
        for (int i = 0; i < paramNames.length; i++) {
            paramMap.put(paramNames[i], paramValues[i]);
        }
        //转换请求参数为字符串
        String param = JSON.toJSONString(paramMap);
        //获取userId
        String userId = iBaseLoginService.getUserId();
        //组装key值，锁名称 + userId + 请求参数
        return CacheKeyEnum.getKey(noRepeat.lockName(), userId, param);
    }

}
